package redis

import (
	"context"
	"errors"
	"fmt"
	"iot-base/common/config"
	"iot-base/common/logger"
	"reflect"
	"time"

	"github.com/go-redis/redis/v8"
	"github.com/vmihailenco/msgpack/v5"
	//"github.com/goinggo/mapstructure"
)

//type redisClient redis.Client

var rc *redis.Client
var ctx = context.Background()

func GetRCClient() *redis.Client {
	return rc
}
func GetRCContext() context.Context {
	return ctx
}
//IsRedisNil 是否为空
func IsRedisNil(err error) bool {
	return err == redis.Nil
}

func structToMap(data map[string]interface{}, obj interface{}) map[string]interface{} {
	t := reflect.TypeOf(obj)
	v := reflect.ValueOf(obj)
	if v.Kind() == reflect.Ptr {
		t = t.Elem()
		v = reflect.Indirect(v)
	}

	if data == nil {
		data = make(map[string]interface{})
	}
	for i := 0; i < t.NumField(); i++ {
		fv := v.Field(i)
		if !fv.CanInterface() {
			continue
		}
		si := fv.Interface()
		sv := reflect.ValueOf(si)
		skind := sv.Kind()
		if skind == reflect.Map || skind == reflect.Ptr || skind == reflect.Slice {
			continue
		}

		if skind == reflect.Struct {
			structToMap(data, si)
		} else {
			data[t.Field(i).Name] = si
		}

	}
	return data
}

func init() {
	rct := redis.NewClient(&redis.Options{
		Addr:       config.DBOption.RedisOption.Addr,
		Password:   config.DBOption.RedisOption.Password, // no password set
		DB:         config.DBOption.RedisOption.Index,
		MaxRetries: 2,
		//PoolTimeout: 10 * time.Second,
		//PoolSize:     50, // use default DB 默认为CPU数量乘以4，不需要设置
		MinIdleConns: 2,
		//IdleTimeout: time.Minute * 5,
	})
	//rct.Select(config.DBOption.RedisOption.Index)
	rc = rct
}

//GetHashObj 从hash中获取obj
func GetHashObj(key string, obj interface{}) (bool, error, *redis.StringStringMapCmd) {
	res := rc.HGetAll(ctx, key)
	err := res.Err()
	if err != nil {
		fmt.Println("gethashobj error----------", err)
		return false, err, res
	}
	/*if err := mapstructure.Decode(res, obj); err != nil {
		return true, err
	}*/
	result, _ := res.Result()
	if len(result) == 0 {
		//fmt.Println("gethashobj-----------get len = 0---2", result, key)
		return false, nil, res
	}
	if obj != nil {
		err = res.Scan(obj)
	}

	return true, err, res
}

//GetHashField 从hash中获取obj
func GetHashField(key string, fieldName string) *redis.StringCmd {
	return rc.HGet(ctx, key, fieldName)
}

//RemoveKey 删除key
func RemoveKey(key string) (int64, error) {
	return rc.Del(ctx, key).Result()
}

//SRem 删除item
func SRem(key string, item string) (int64, error) {
	return rc.SRem(ctx, key, item).Result()
}

//SetHashObj 将Obj存储到hash中去
func SetHashObj(key string, obj interface{}) error {
	maps := structToMap(nil, obj)
	if len(maps) == 0 {
		fmt.Println("maps len is 0------------------")
		return errors.New("map is empty")
	}
	//delete(maps, "DBObjIDName")
	_, err := rc.HMSet(ctx, key, maps).Result()
	return err
}

//HSetField 设置值
func HSetField(key string, values ...interface{}) error {
	_, err := rc.HSet(ctx, key, values...).Result()
	return err
}

//HSetMap 设置key的值
func HSetMap(key string, values map[string]interface{}) error {
	_, err := rc.HMSet(ctx, key, values).Result()
	return err
}

//HDelField 删除field
func HDelField(key string, fieldName string) error {
	res := rc.HDel(ctx, key, fieldName)
	return res.Err()
}
//HLen 长度
func HLen(key string) (int64, error) {
	res := rc.HLen(ctx, key)
	if res == nil {
		return 0, errors.New("redis nil error")
	}
	return res.Result()
}

//InsertObj 插入元素
func InsertObj(key string, value interface{}, limit int64) {
	v, _ := msgpack.Marshal(value)
	errPush := rc.RPush(ctx, key, v).Err()
	if errPush == nil {
		count, err := rc.LLen(ctx, key).Result()
		if err == nil {
			if limit != -1 && count > limit {
				rc.LTrim(ctx, key, count-limit, -1)
			}

		}
	} else {
		logger.SLog.Error("insert data error ", "error", errPush)
	}

}

//Lindex 获取指定index的值
func Lindex(key string, index int, target interface{}) error {
	str, err := rc.LIndex(ctx, key, int64(index)).Result()
	if err != nil {
		return err
	}
	err = msgpack.Unmarshal([]byte(str), target)
	return err

}

//GetLastValue 获取insertobj插入的最新数据
func GetLastValue(listname string, target interface{}) error {
	return Lindex(listname, -1, target)
}

//GetList 获取list数据
func GetList(key string) ([]interface{}, error) {
	list, err := rc.LRange(ctx, key, 0, -1).Result()
	if err == nil {
		result := make([]interface{}, len(list))
		for i := range list {
			var obj map[string]interface{}
			msgpack.Unmarshal([]byte(list[i]), &obj)
			result[i] = obj
		}
		return result, nil
	}
	return nil, err
}

//GetListByPattern 通过模糊查询查询数据
func GetListByPattern(key string) ([]interface{}, error) {
	listkey, err := rc.Keys(ctx, key).Result()

	if err == nil {
		result := make([]interface{}, 0)
		for _, key := range listkey {
			list, _ := GetList(key)
			if list != nil {
				result = append(result, list...)
			}
		}
		return result, nil
	}
	return nil, err
}

//GetLastObj 获取list中的最后一个元素
func GetLastObj(key string, lastV interface{}) error {
	lastv, err := rc.LIndex(ctx, key, -1).Result()
	if err == nil {
		//var lastV map[string]interface{}
		err = msgpack.Unmarshal([]byte(lastv), lastV)
		return err
	}
	return err
}

//AddScore 增加分数set成员
func AddScore(key string, id string, score float64) {
	item := &redis.Z{
		Score:  score,
		Member: id,
	}
	rc.ZAdd(ctx, key, item)
}

//RemoveKeyFromSet 删除key
func RemoveKeyFromSet(key string, id string) {
	rc.ZRem(ctx, key, id)
}

//ZRangeByScore 使用score获取key
func ZRangeByScore(key string, min float64, max float64) ([]string, error) {
	opt := &redis.ZRangeBy{
		Min: fmt.Sprintf("%f", min),
		Max: fmt.Sprintf("%f", max),
	}
	return rc.ZRangeByScore(ctx, key, opt).Result()
}

//DelByKeyPattern 删除key
func DelByKeyPattern(key string) error {
	keys, err := rc.Keys(ctx, key).Result()
	if err != nil {
		return err
	}
	for _, key := range keys {
		rc.Del(ctx, key)
	}
	return nil
}

//SScan rc 中的sscan
func SScan(key string, cursor uint64, match string, count int64) *redis.ScanCmd {
	return rc.SScan(ctx, key, cursor, match, count)
}

func Scan(cursor uint64, match string, count int64) *redis.ScanCmd {
	return rc.Scan(ctx, cursor, match, count)
}

//Set
func Set(key string, value interface{}, expiration time.Duration) *redis.StatusCmd {
	return rc.Set(ctx, key, value, expiration)
}

//Get
func Get(key string) *redis.StringCmd {
	return rc.Get(ctx, key)
}

//SIsMember( key string, member interface{}) *BoolCmd
func SIsMember(key string, member interface{}) *redis.BoolCmd {
	return rc.SIsMember(ctx, key, member)
}

//SCard get set len
func SCard(key string) *redis.IntCmd {
	return rc.SCard(ctx, key)
}

//SetNX func (c Client) SetNX( key string, value interface{}, expiration time.Duration) *BoolCmd
func SetNX(key string, value interface{}, expiration time.Duration) *redis.BoolCmd {
	return rc.SetNX(ctx, key, value, expiration)
}

//Del( keys ...string) *IntCmd
func Del(keys ...string) *redis.IntCmd {
	return rc.Del(ctx, keys...)
}

//TTl func (c ClusterClient) TTL( key string) *DurationCmd
func TTL(key string) *redis.DurationCmd {
	return rc.TTL(ctx, key)
}

//func (c Client) HSet( key string, values ...interface{}) *IntCmd
func HSet(key string, values ...interface{}) *redis.BoolCmd {
	return rc.HMSet(ctx, key, values)
}

func HHSet(key string, values ...interface{}) (int64, error) {
	return rc.HSet(ctx, key, values...).Result()
}

//func (c ClusterClient) Exists( keys ...string) *IntCmd
func Exists(keys ...string) *redis.IntCmd {
	return rc.Exists(ctx, keys...)
}

//func (c ClusterClient) Keys( pattern string) *StringSliceCmd
func Keys(pattern string) *redis.StringSliceCmd {
	return rc.Keys(ctx, pattern)
}

//func (c ClusterClient) LRange( key string, start, stop int64) *StringSliceCmd
func LRange(key string, start, stop int64) *redis.StringSliceCmd {
	return rc.LRange(ctx, key, start, stop)
}

//HGetAll(ctx context.Context, key string) *StringStringMapCmd
func HGetAll(key string) *redis.StringStringMapCmd {
	return rc.HGetAll(ctx, key)
}

func HGet(key string, field string) *redis.StringCmd {
	return rc.HGet(ctx, key, field)
}

//HKeys 获取keys
func HKeys(key string) ([]string, error) {
	res := rc.HKeys(ctx, key)
	if res == nil {
		return nil, errors.New("redis return nil")
	}
	return res.Result()
}

//SAdd(ctx context.Context, key string, members ...interface{}) *IntCmd
func SAdd(key string, members ...interface{}) *redis.IntCmd {
	return rc.SAdd(ctx, key, members...)
}

func SMembers(key string) *redis.StringSliceCmd {
	return rc.SMembers(ctx, key)
}

//IsNotFount 是否为找不到错误
func IsNotFound(err error) bool {
	return err == redis.Nil
}

//IncrBy 增加
func IncrBy(key string, count int64) *redis.IntCmd {
	return rc.IncrBy(ctx, key, count)
}

func IncrByFloat(key string, incr float64) *redis.FloatCmd {
	return rc.IncrByFloat(ctx, key, incr)
}

//HIncrBy 增加
func HIncrBy(key string, field string, incr int64) *redis.IntCmd {
	return rc.HIncrBy(ctx, key, field, incr)
}
//HIncrByFloat
func HIncrByFloat(key string, field string, incr float64) *redis.FloatCmd {
	return rc.HIncrByFloat(ctx, key, field, incr)
}
//Expire
func Expire(key string, exp time.Duration) *redis.BoolCmd {
	return rc.Expire(ctx, key, exp)
}
//ZScore
func ZScore(key string, member string) (float64, error) {
	return rc.ZScore(ctx, key, member).Result()
}
//RPushTimeOut
func RPushTimeOut(key string, data interface{}, exp int) error {
	err := rc.RPush(ctx, key, data).Err()
	if err == nil {
		err = rc.Expire(ctx, key, time.Second*time.Duration(exp)).Err()
	}
	return err
}
//BLPop
func BLPop(exr int, keys ...string) ([]string, error) {
	return rc.BLPop(ctx, time.Second*time.Duration(exr), keys...).Result()
}
